﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;

namespace Seven.Tools.Extension
{
    /// <summary>
    /// 通用类型扩展方法类
    /// </summary>
    public static class ObjectExtensions
    {
        /// <summary>
        /// 判断指定的对象是否为 Null 或者等于 DBNull.Value 值。
        /// </summary>
        /// <param name="_this"></param>
        /// <returns></returns>
        public static bool IsNull(this Object _this)
        {
            return _this == null || _this.Equals(DBNull.Value);
        }

        #region CastTo

        /// <summary>
        /// 将当前对象转换成一个指定类型的新对象。
        /// 该方法首先判断 <paramref name="_this"/> 是否为 <paramref name="resultType"/> 指定的类型，如果是，则直接返回 <paramref name="_this"/>；
        /// 否则将通过 Activator.CreateInstance(resultType) 方法创建一个 <paramref name="resultType"/> 类型的对象，然后查找出该类型对象有哪些公共属性值，将 <paramref name="_this"/> 对象的对应属性值复制到新创建的对象中。
        /// </summary>
        /// <param name="_this">要转换的对象。</param>
        /// <param name="resultType">要转换的目标实体类型。</param>
        /// <param name="throwError">一个布尔类型值，该值表示在复制数据过程中如果出现异常，是否立即停止并抛出异常，默认为 false。</param>
        /// <returns>
        /// 该方法首先判断 <paramref name="_this"/> 是否为 <paramref name="resultType"/> 指定的类型，如果是，则直接返回 <paramref name="_this"/>；
        /// 否则将通过 Activator.CreateInstance(resultType) 方法创建一个 <paramref name="resultType"/> 类型的对象，然后查找出该类型对象有哪些公共属性值，将 <paramref name="_this"/> 对象的对应属性值复制到新创建的对象中。
        /// </returns>
        public static object CastTo(this object _this, Type resultType, bool throwError = false)
        {
            if (resultType.IsInstanceOfType(_this))
            {
                return _this;
            }
            object ret = Activator.CreateInstance(resultType);
            _this.CopyTo(ret, throwError);
            return ret;
        }

        /// <summary>
        /// 将当前对象转换成一个指定类型的新对象。
        /// 该方法首先判断 <paramref name="_this"/> 是否为 <typeparamref name="TResult"/> 指定的类型，如果是，则直接返回 <paramref name="_this"/>；
        /// 否则将通过 Activator.CreateInstance(resultType) 方法创建一个 <typeparamref name="TResult"/> 类型的对象，然后查找出该类型对象有哪些公共属性值，将 <paramref name="_this"/> 对象的对应属性值复制到新创建的对象中。
        /// </summary>
        /// <typeparam name="TResult">要转换的目标实体类型。</typeparam>
        /// <param name="_this">要转换的对象。</param>
        /// <param name="throwError">一个布尔类型值，该值表示在复制数据过程中如果出现异常，是否立即停止并抛出异常，默认为 false。</param>
        /// <returns>
        /// 该方法首先判断 <paramref name="_this"/> 是否为 <typeparamref name="TResult"/> 指定的类型，如果是，则直接返回 <paramref name="_this"/>；
        /// 否则将通过 Activator.CreateInstance(resultType) 方法创建一个 <typeparamref name="TResult"/> 类型的对象，然后查找出该类型对象有哪些公共属性值，将 <paramref name="_this"/> 对象的对应属性值复制到新创建的对象中。
        /// </returns>
        public static TResult CastTo<TResult>(this object _this, bool throwError = false)
        {
            if (_this is TResult)
            {
                return (TResult)_this;
            }
            TResult ret = (TResult)Activator.CreateInstance(typeof(TResult));
            _this.CopyTo(ret, throwError);
            return ret;
        }

        #endregion

        #region CopyTo

        /// <summary>
        /// 将当前对象中所有属性的值按照属性名称和类型定义的匹配关系复制到另一对象中。
        /// </summary>
        /// <param name="_this">
        /// 表示将要用于复制数据到另一对象的元数据对象。
        /// 如果该参数值为 Null，将不会执行复制操作。
        /// </param>
        /// <param name="element">表示一个目标对象，该对象中的相应属性值将会被更改。</param>
        /// <param name="throwError">一个布尔类型值，该值表示在复制数据过程中如果出现异常，是否立即停止并抛出异常，默认为 false。</param>
        public static void CopyTo(this object _this, object element, bool throwError = false)
        {
            if (_this == null)
            {
                return;
            }
            Type thisType = _this.GetType(), elemType = element.GetType();
            var thisProps = thisType.GetProperties().Where(p => p.GetMethod != null);
            var elemProps = elemType.GetProperties().Where(p => p.SetMethod != null);
            foreach (PropertyInfo thisProperty in thisProps)
            {
                PropertyInfo elemProperty = elemProps.FirstOrDefault(p => p.Name == thisProperty.Name);
                if (elemProperty != null && elemProperty.PropertyType.IsAssignableFrom(thisProperty.PropertyType))
                {
                    Utility.TryCatchExecute(() => elemProperty.SetValue(element, thisProperty.GetValue(_this)));
                }
            }
        }

        /// <summary>
        /// 将当前对象的属性值复制到目标对象，使用浅表复制
        /// </summary>
        /// <typeparam name="T">目标对象类型</typeparam>
        /// <param name="source">源对象</param>
        /// <param name="target">目标对象，如果为空，将生成一个</param>
        /// <returns>复制过后的目标对象</returns>
        public static T CopyTo<T>(this object source, T target = null, string[] filter = null) where T : class,new()
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (target == null)
                target = new T();
            ModuleCast.GetCast(source.GetType(), typeof(T)).Cast(source, target, filter);
            return target;
        }

        #endregion

        #region Duplicate

        /// <summary>
        /// 创建一个当前对象的新副本，该副本和当前对象为同一类型，且其中各个属性的值均等同于原对象中各个属性的值。
        /// 相当于浅表复制操作 Object.MemberwiseClone，但和 Object.MemberwiseClone 不同的是该操作只对公共 Public 的可 set 属性进行复制，并且不会复制其他字段或私有成员的值。
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="_this"></param>
        /// <param name="throwError"></param>
        /// <returns></returns>
        public static TSource Duplicate<TSource>(this TSource _this, bool throwError = false) where TSource : class, new()
        {
            if (_this == null)
                return null;

            Type thisType = typeof(TSource);
            if (thisType.IsAbstract)
            {
                Object obj = Duplicate(_this, throwError);
                return obj == null ? null : obj as TSource;
            }

            if (thisType.IsValueType)
                return _this;

            if (_this is ICloneable)
                return ((ICloneable)_this).Clone() as TSource;

            TSource source = new TSource();
            _this.CopyTo(source, throwError);
            return source;
        }

        #endregion
    }




    /// <summary>
    /// 属性转换类，将一个类的属性值转换给另外一个类的同名属性，注意该类使用的是浅表复制。
    /// <example>
    ///        下面几种用法一样:
    ///        ModuleCast.GetCast(typeof(CarInfo), typeof(ImplCarInfo)).Cast(info, ic);
    ///        ModuleCast.CastObject《CarInfo, ImplCarInfo》(info, ic);
    ///        ModuleCast.CastObject(info, ic);
    ///
    ///        ImplCarInfo icResult= info.CopyTo《ImplCarInfo》(null);
    ///
    ///        ImplCarInfo icResult2 = new ImplCarInfo();
    ///        info.CopyTo《ImplCarInfo》(icResult2);
    /// 
    /// </example>
    /// </summary>
    internal class ModuleCast
    {
        private List<CastProperty> mProperties = new List<CastProperty>();

        static Dictionary<Type, Dictionary<Type, ModuleCast>> mCasters = new Dictionary<Type, Dictionary<Type, ModuleCast>>(256);

        private static Dictionary<Type, ModuleCast> GetModuleCast(Type sourceType)
        {
            Dictionary<Type, ModuleCast> result;
            lock (mCasters)
            {
                if (!mCasters.TryGetValue(sourceType, out result))
                {
                    result = new Dictionary<Type, ModuleCast>(8);
                    mCasters.Add(sourceType, result);
                }
            }
            return result;
        }

        /// <summary>
        /// 获取要转换的当前转换类实例
        /// </summary>
        /// <param name="sourceType">要转换的源类型</param>
        /// <param name="targetType">目标类型</param>
        /// <returns></returns>
        public static ModuleCast GetCast(Type sourceType, Type targetType)
        {
            Dictionary<Type, ModuleCast> casts = GetModuleCast(sourceType);
            ModuleCast result;
            lock (casts)
            {
                if (!casts.TryGetValue(targetType, out result))
                {
                    result = new ModuleCast(sourceType, targetType);
                    casts.Add(targetType, result);
                }
            }
            return result;
        }

        /// <summary>
        /// 以两个要转换的类型作为构造函数，构造一个对应的转换类
        /// </summary>
        /// <param name="sourceType"></param>
        /// <param name="targetType"></param>
        public ModuleCast(Type sourceType, Type targetType)
        {
            PropertyInfo[] targetProperties = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (PropertyInfo sp in sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                foreach (PropertyInfo tp in targetProperties)
                {
                    //作者版本：if (sp.Name == tp.Name && sp.PropertyType == tp.PropertyType)
                    //matrixkey修改：if (sp.Name == tp.Name && (sp.PropertyType == tp.PropertyType || Nullable.GetUnderlyingType(tp.PropertyType) == sp.PropertyType))
                    //区别，修改后的版本增加了“源实例中属性类型为int而目标实例中属性类型为int?的情况下也进行复制”的判定
                    if (sp.Name == tp.Name && (sp.PropertyType == tp.PropertyType || Nullable.GetUnderlyingType(tp.PropertyType) == sp.PropertyType))
                    {
                        CastProperty cp = new CastProperty();
                        cp.SourceProperty = new PropertyAccessorHandler(sp);
                        cp.TargetProperty = new PropertyAccessorHandler(tp);
                        mProperties.Add(cp);
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// 将源类型的属性值转换给目标类型同名的属性
        /// </summary>
        /// <param name="source"></param>
        /// <param name="target"></param>
        public void Cast(object source, object target)
        {
            Cast(source, target, null);
        }

        /// <summary>
        /// 将源类型的属性值转换给目标类型同名的属性，排除要过滤的属性名称
        /// </summary>
        /// <param name="source"></param>
        /// <param name="target"></param>
        /// <param name="filter">要过滤的属性名称</param>
        public void Cast(object source, object target, string[] filter)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (target == null)
                throw new ArgumentNullException("target");

            for (int i = 0; i < mProperties.Count; i++)
            {
                CastProperty cp = mProperties[i];

                if (cp.SourceProperty.Getter != null)
                {
                    object Value = cp.SourceProperty.Getter(source, null); //PropertyInfo.GetValue(source,null);
                    if (cp.TargetProperty.Setter != null)
                    {
                        if (filter == null)
                            cp.TargetProperty.Setter(target, Value, null);
                        else if (!filter.Contains(cp.TargetProperty.PropertyName))
                            cp.TargetProperty.Setter(target, Value, null);

                    }
                }
            }
        }

        /// <summary>
        /// 转换对象
        /// </summary>
        /// <typeparam name="TSource">源类型</typeparam>
        /// <typeparam name="TTarget">目标类型</typeparam>
        /// <param name="source">源对象</param>
        /// <param name="target">目标对象</param>
        public static void CastObject<TSource, TTarget>(TSource source, TTarget target)
            where TSource : class
            where TTarget : class
        {
            ModuleCast.GetCast(typeof(TSource), typeof(TTarget)).Cast(source, target);
        }


        /// <summary>
        /// 转换属性对象
        /// </summary>
        public class CastProperty
        {
            public PropertyAccessorHandler SourceProperty
            {
                get;
                set;
            }

            public PropertyAccessorHandler TargetProperty
            {
                get;
                set;
            }
        }

        /// <summary>
        /// 属性访问器
        /// </summary>
        public class PropertyAccessorHandler
        {
            public PropertyAccessorHandler(PropertyInfo propInfo)
            {
                this.PropertyName = propInfo.Name;
                //var obj = Activator.CreateInstance(classType);
                //var getterType = typeof(FastPropertyAccessor.GetPropertyValue<>).MakeGenericType(propInfo.PropertyType);
                //var setterType = typeof(FastPropertyAccessor.SetPropertyValue<>).MakeGenericType(propInfo.PropertyType);

                //this.Getter = Delegate.CreateDelegate(getterType, null, propInfo.GetGetMethod());
                //this.Setter = Delegate.CreateDelegate(setterType, null, propInfo.GetSetMethod());
                if (propInfo.CanRead)
                    this.Getter = propInfo.GetValue;

                if (propInfo.CanWrite)
                    this.Setter = propInfo.SetValue;
            }
            public string PropertyName { get; set; }
            public Func<object, object[], object> Getter { get; private set; }
            public Action<object, object, object[]> Setter { get; private set; }
        }
    }
}
